package com.gwtintegration.smartrf.client;

import java.util.ArrayList;

import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.gwtintegration.smartrf.client.data.CustomType;
import com.gwtintegration.smartrf.shared.DataRequestCriterionValueProxy;
import com.gwtintegration.smartrf.shared.DataRequestOperatorId;
import com.gwtintegration.smartrf.shared.DataRequestSortDirection;
import com.gwtintegration.smartrf.shared.DataRequestSortSpecifierValueProxy;
import com.gwtintegration.smartrf.shared.DataRequestTextMatchStyle;
import com.gwtintegration.smartrf.shared.DataRequestValueProxy;
import com.gwtintegration.smartrf.shared.RequestFactoryEncoder;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.Criterion;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.DataSourceField;
import com.smartgwt.client.data.SortSpecifier;

public class RequestFactoryRequestConverter {

	private final DataSource dataSource;

	public RequestFactoryRequestConverter(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public DataRequestValueProxy convert(DSRequest dsRequest, RequestContext requestContext) {

		DataRequestValueProxy dataRequest = requestContext.create(DataRequestValueProxy.class);

		Criteria criteria = dsRequest.getCriteria();
		if (criteria != null) {
			dataRequest.setCriteria(getCriteria(requestContext, criteria.asAdvancedCriteria()));
		}

		dataRequest.setStartRow(dsRequest.getStartRow());
		dataRequest.setEndRow(dsRequest.getEndRow());

		dataRequest.setTextMatchStyle(getTextMatchStyle(dsRequest));
		dataRequest.setSortBy(getSortBy(dsRequest, requestContext));

		return dataRequest;
	}

	private ArrayList<DataRequestSortSpecifierValueProxy> getSortBy(DSRequest request, RequestContext requestContext) {

		ArrayList<DataRequestSortSpecifierValueProxy> dataRequestSortBy = new ArrayList<DataRequestSortSpecifierValueProxy>();
		SortSpecifier[] sortBy = request.getSortBy();
		if (sortBy != null) {
			for (SortSpecifier sortSpecifier : sortBy) {

				DataRequestSortSpecifierValueProxy dataRequestSort = requestContext.create(DataRequestSortSpecifierValueProxy.class);

				dataRequestSort.setFieldName(sortSpecifier.getField());
				dataRequestSort.setDirection(getSortByDirection(sortSpecifier));

				dataRequestSortBy.add(dataRequestSort);
			}
		}
		return dataRequestSortBy;
	}

	private DataRequestCriterionValueProxy getCriteria(RequestContext requestContext, Criteria criteria) {

		if (criteria == null) {
			return null;
		}

		if (criteria instanceof Criterion) {
			Criterion criterion = (Criterion) criteria;

			if (criterion.getCriteria() != null) {

				DataRequestCriterionValueProxy filter = requestContext.create(DataRequestCriterionValueProxy.class);
				filter.setOperator(getOperator(criterion));
				filter.setFieldName(criterion.getFieldName());

				filter.setCriterions(new ArrayList<DataRequestCriterionValueProxy>());

				for (Criterion subCriterion : criterion.getCriteria()) {
					DataRequestCriterionValueProxy subFilter = getCriteria(requestContext, subCriterion);

					if (subFilter != null) {
						filter.getCriterions().add(subFilter);
					}
				}

				// Return this filter only if we found something
				if (!filter.getCriterions().isEmpty()) {
					return filter;
				}

			} else if (criterion.getFieldName() != null) {

				DataRequestCriterionValueProxy filter = requestContext.create(DataRequestCriterionValueProxy.class);

				filter.setOperator(getOperator(criterion));
				filter.setFieldName(criterion.getFieldName());

				Object value = getValue(dataSource.getField(criterion.getFieldName()), criterion);
				filter.setValue(RequestFactoryEncoder.getString(value));

				return filter;
			}
		}

		return null;
	}

	private Object getValue(DataSourceField dataSourceField, Criterion criterion) {
		if (dataSourceField != null) {
			if (dataSourceField.getType() != null) {
				switch (dataSourceField.getType()) {
					case TEXT:
						return criterion.getValueAsString();
					case INTEGER:
						return criterion.getValueAsInteger();
					case FLOAT:
						return criterion.getValueAsFloat();
					case DATE:
					case TIME:
					case DATETIME:
						return criterion.getValueAsDate();
					case BOOLEAN:
						return criterion.getValueAsBoolean();
					default:
						return criterion.getAttributeAsObject("value");
				}
			} else {
				if (CustomType.LONG.getName().equals(dataSourceField.getAttribute("type"))) {
					return criterion.getAttributeAsLong("value");
				} else if (CustomType.DOUBLE.getName().equals(dataSourceField.getAttribute("type"))) {
					return criterion.getAttributeAsDouble("value");
				}
			}
		}

		// Trying to guess the field type.
		return criterion.getAttributeAsObject("value");
	}

	private DataRequestOperatorId getOperator(Criterion criterion) {
		if (criterion.getOperator() == null) {
			return null;
		}

		return DataRequestOperatorId.valueOf(criterion.getOperator().name());
	}

	private DataRequestSortDirection getSortByDirection(SortSpecifier sortSpecifier) {
		if (sortSpecifier.getSortDirection() == null) {
			return DataRequestSortDirection.ASCENDING;
		}

		return DataRequestSortDirection.valueOf(sortSpecifier.getSortDirection().name());
	}

	private DataRequestTextMatchStyle getTextMatchStyle(DSRequest request) {

		if (request.getTextMatchStyle() == null) {
			return DataRequestTextMatchStyle.EXACT;
		}

		return DataRequestTextMatchStyle.valueOf(request.getTextMatchStyle().name());
	}

}
